<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>WebRTC屏幕共享</title>
    <script src="https://cdn.tailwindcss.com"></script>
    <link href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css" rel="stylesheet">
    <script>
        tailwind.config = {
            theme: {
                extend: {
                    colors: {
                        primary: '#165DFF',
                        secondary: '#36D399',
                        danger: '#F87272',
                        neutral: '#3D4451',
                    },
                    fontFamily: {
                        inter: ['Inter', 'system-ui', 'sans-serif'],
                    },
                }
            }
        }
    </script>
    <style type="text/tailwindcss">
        @layer utilities {
            .content-auto {
                content-visibility: auto;
            }
            .backdrop-blur-sm {
                backdrop-filter: blur(4px);
            }
            .transition-transform-opacity {
                transition-property: transform, opacity;
                transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
                transition-duration: 300ms;
            }
            .video-container {
                aspect-ratio: 16/9;
            }
        }
    </style>
</head>
<body class="bg-gray-100 font-inter min-h-screen flex flex-col">
    <!-- 房间选择页面 -->
    <div id="room-selection" class="flex-grow flex items-center justify-center p-4">
        <div class="w-full max-w-md bg-white rounded-2xl shadow-xl p-8 transform transition-all duration-300 hover:shadow-2xl">
            <div class="text-center mb-8">
                <div class="inline-block p-4 bg-primary/10 rounded-full mb-4">
                    <i class="fa fa-video-camera text-3xl text-primary"></i>
                </div>
                <h1 class="text-[clamp(1.5rem,3vw,2.5rem)] font-bold text-neutral">WebRTC屏幕共享</h1>
                <p class="text-gray-500 mt-2">通过房间号加入或创建屏幕共享会话</p>
            </div>
            
            <form id="room-form" class="space-y-6">
                <div>
                    <label for="room-id" class="block text-sm font-medium text-gray-700 mb-1">房间号</label>
                    <div class="relative">
                        <div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
                            <i class="fa fa-hashtag text-gray-400"></i>
                        </div>
                        <input type="text" id="room-id" name="room-id" 
                               class="pl-10 block w-full rounded-lg border-gray-300 shadow-sm focus:ring-primary focus:border-primary sm:text-sm border py-3"
                               placeholder="输入或创建房间号" required>
                    </div>
                </div>
                
                <button type="submit" class="w-full flex justify-center py-3 px-4 border border-transparent rounded-lg shadow-sm text-sm font-medium text-white bg-primary hover:bg-primary/90 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary transition-all transform hover:scale-[1.02]">
                    <i class="fa fa-sign-in mr-2"></i>
                    进入房间
                </button>
            </form>
            
            <div class="mt-8 text-center text-sm text-gray-500">
                <p>提示: 分享相同的房间号给对方以开始屏幕共享</p>
            </div>
        </div>
    </div>

    <!-- 屏幕共享主页面 (初始隐藏) -->
    <div id="screen-share" class="hidden flex-grow flex flex-col">
        <header class="bg-white shadow-md">
            <div class="container mx-auto px-4 py-4 flex justify-between items-center">
                <div class="flex items-center gap-2">
                    <i class="fa fa-video-camera text-primary text-xl"></i>
                    <h1 class="text-[clamp(1.2rem,2vw,1.8rem)] font-bold text-neutral">
                        <span id="room-title">房间 #12345</span>
                    </h1>
                </div>
                
                <div class="flex items-center gap-4">
                    <span id="connection-status" class="text-sm px-3 py-1 rounded-full bg-gray-100 text-gray-600 flex items-center">
                        <i class="fa fa-circle text-yellow-500 mr-2 text-xs"></i>
                        等待连接
                    </span>
                    
                    <button id="leave-room" class="px-4 py-2 bg-danger hover:bg-danger/90 text-white rounded-lg flex items-center gap-2 transition-all transform hover:scale-105">
                        <i class="fa fa-sign-out"></i>
                        <span>离开房间</span>
                    </button>
                </div>
            </div>
        </header>

        <main class="flex-grow container mx-auto px-4 py-8">
            <div class="max-w-6xl mx-auto">
                <!-- 状态卡片 -->
                <div class="bg-white rounded-xl shadow-lg p-6 mb-8 transition-all duration-300 hover:shadow-xl">
                    <div id="main-status-card" class="flex items-center justify-center py-4">
                        <div class="text-center">
                            <div class="inline-block p-4 bg-primary/10 rounded-full mb-4">
                                <i class="fa fa-info-circle text-3xl text-primary"></i>
                            </div>
                            <h2 id="main-status-text" class="text-[clamp(1.2rem,2vw,1.5rem)] font-medium text-neutral">
                                等待对方加入房间
                            </h2>
                            <p class="text-gray-500 mt-2">请确保已与对方共享房间号</p>
                        </div>
                    </div>
                </div>

                <!-- 视频显示区域 -->
                <div class="grid grid-cols-1 lg:grid-cols-3 gap-6 mb-8">
                    <!-- 主视频窗口 -->
                    <div class="lg:col-span-2 relative bg-gray-900 rounded-xl overflow-hidden shadow-xl">
                        <div id="main-video-container" class="video-container relative cursor-pointer">
                            <video id="remote-video" class="w-full h-full object-contain" autoplay></video>
                            
                            <!-- 初始提示覆盖层 -->
                            <div id="remote-overlay" class="absolute inset-0 bg-black/60 flex items-center justify-center">
                                <div class="text-center p-6">
                                    <div class="inline-block p-4 bg-white/10 rounded-full mb-4 backdrop-blur-sm">
                                        <i class="fa fa-spinner fa-spin text-5xl text-white/80"></i>
                                    </div>
                                    <h3 class="text-xl font-medium text-white mb-2">等待对方共享内容</h3>
                                    <p class="text-white/70">对方加入并开始共享后，内容将显示在这里</p>
                                </div>
                            </div>
                            
                            <!-- 视频控制条 -->
                            <div class="absolute bottom-0 left-0 right-0 bg-gradient-to-t from-black/80 to-transparent p-4 opacity-0 hover:opacity-100 transition-opacity duration-300 flex justify-between items-center">
                                <div class="text-white/80 text-sm">
                                    <span id="remote-title">对方的屏幕</span>
                                </div>
                                <div class="flex gap-2">
                                    <button id="toggle-fullscreen" class="p-2 bg-white/20 hover:bg-white/30 rounded-full text-white transition-all">
                                        <i class="fa fa-expand"></i>
                                    </button>
                                </div>
                            </div>
                        </div>
                    </div>
                    
                    <!-- 本地视频窗口 -->
                    <div class="relative bg-gray-900 rounded-xl overflow-hidden shadow-xl">
                        <div id="local-video-container" class="video-container relative cursor-pointer hover:ring-4 hover:ring-primary transition-all">
                            <video id="local-video" class="w-full h-full object-contain" autoplay muted></video>
                            
                            <!-- 初始提示覆盖层 -->
                            <div id="local-overlay" class="absolute inset-0 bg-black/60 flex items-center justify-center">
                                <div class="text-center p-6">
                                    <div class="inline-block p-4 bg-white/10 rounded-full mb-4 backdrop-blur-sm">
                                        <i class="fa fa-play-circle text-5xl text-white/80"></i>
                                    </div>
                                    <h3 class="text-xl font-medium text-white mb-2">点击开始共享</h3>
                                    <p class="text-white/70">点击下方按钮选择要共享的屏幕内容</p>
                                </div>
                            </div>
                            
                            <!-- 视频控制条 -->
                            <div class="absolute bottom-0 left-0 right-0 bg-gradient-to-t from-black/80 to-transparent p-4 opacity-0 hover:opacity-100 transition-opacity duration-300 flex justify-between items-center">
                                <div class="text-white/80 text-sm">
                                    <span id="local-title">我的屏幕</span>
                                </div>
                                <div class="flex gap-2">
                                    <button id="start-share-btn" class="px-4 py-2 bg-primary hover:bg-primary/90 text-white rounded-lg flex items-center gap-2 transition-all">
                                        <i class="fa fa-desktop"></i>
                                        <span>开始共享</span>
                                    </button>
                                </div>
                            </div>
                            
                            <!-- 交换按钮 -->
                            <div class="absolute top-4 right-4">
                                <button id="swap-video-btn" class="p-3 bg-primary/90 hover:bg-primary text-white rounded-full shadow-lg transition-all transform hover:scale-110">
                                    <i class="fa fa-exchange"></i>
                                </button>
                            </div>
                        </div>
                    </div>
                </div>

                <!-- 信息卡片 -->
                <div class="bg-white rounded-xl shadow-lg p-6 mb-8">
                    <h3 class="text-xl font-medium text-neutral mb-4 flex items-center">
                        <i class="fa fa-info-circle text-primary mr-2"></i>
                        使用说明
                    </h3>
                    <ul class="space-y-2 text-gray-600">
                        <li class="flex items-start">
                            <i class="fa fa-check-circle text-secondary mt-1 mr-2"></i>
                            <span>点击"开始共享"按钮选择要共享的内容（整个屏幕、窗口或浏览器标签）</span>
                        </li>
                        <li class="flex items-start">
                            <i class="fa fa-check-circle text-secondary mt-1 mr-2"></i>
                            <span>大窗口显示对方的共享内容，小窗口显示您的共享内容</span>
                        </li>
                        <li class="flex items-start">
                            <i class="fa fa-exchange text-primary mt-1 mr-2"></i>
                            <span>点击小窗口上的交换按钮可以切换大小窗口显示内容</span>
                        </li>
                        <li class="flex items-start">
                            <i class="fa fa-expand text-primary mt-1 mr-2"></i>
                            <span>双击大窗口或点击全屏按钮可以进入全屏模式</span>
                        </li>
                        <li class="flex items-start">
                            <i class="fa fa-exclamation-triangle text-warning mt-1 mr-2"></i>
                            <span>屏幕共享可能会暴露敏感信息，请确保仅共享适当的内容</span>
                        </li>
                    </ul>
                </div>
            </div>
        </main>

        <footer class="bg-white border-t border-gray-200 py-6">
            <div class="container mx-auto px-4 text-center text-gray-500 text-sm">
                <p>© 2025 WebRTC屏幕共享 | 使用现代Web API构建</p>
            </div>
        </footer>
    </div>

    <!-- JavaScript -->
    <script>
        'use strict';
        // DOM元素
        const roomSelectionPage = document.getElementById('room-selection');
        const screenSharePage = document.getElementById('screen-share');
        const roomForm = document.getElementById('room-form');
        const roomIdInput = document.getElementById('room-id');
        const roomTitle = document.getElementById('room-title');
        const leaveRoomBtn = document.getElementById('leave-room');
        const connectionStatus = document.getElementById('connection-status');
        const mainStatusText = document.getElementById('main-status-text');
        const remoteVideo = document.getElementById('remote-video');
        const localVideo = document.getElementById('local-video');
        const remoteOverlay = document.getElementById('remote-overlay');
        const localOverlay = document.getElementById('local-overlay');
        const startShareBtn = document.getElementById('start-share-btn');
        const swapVideoBtn = document.getElementById('swap-video-btn');
        const toggleFullscreenBtn = document.getElementById('toggle-fullscreen');
        const mainVideoContainer = document.getElementById('main-video-container');
        const localVideoContainer = document.getElementById('local-video-container');
        const remoteTitle = document.getElementById('remote-title');
        const localTitle = document.getElementById('local-title');
        
        const SIGNAL_TYPE_JOIN = "join";
        const SIGNAL_TYPE_RESP_JOIN = "resp-join";  // 告知加入者对方是谁
        const SIGNAL_TYPE_LEAVE = "leave";
        const SIGNAL_TYPE_NEW_PEER = "new-peer";
        const SIGNAL_TYPE_PEER_LEAVE = "peer-leave";
        const SIGNAL_TYPE_OFFER = "offer";
        const SIGNAL_TYPE_ANSWER = "answer";
        const SIGNAL_TYPE_CANDIDATE = "candidate";
        const SIGNAL_TYPE_START = "start";
        const SIGNAL_TYPE_END = "end";

        
        // 应用状态
        let isSwapped = false;
        let localStream = null;
        let remoteStream = null;
        let lastClickTime = 0;
        var localUserId = Math.random().toString(36).substr(2); // 本地uid
        var remoteUserId = -1;      // 对端
        var roomId = 0;
        var pc = null;

        var RTCEngine;
        
        // 房间表单提交
        roomForm.addEventListener('submit', (e) => {
            e.preventDefault();
            roomId = roomIdInput.value;
            
            if (roomId) {
                // 更新房间标题
                roomTitle.textContent = `房间 #${roomId.trim()}`;
                
                // 切换页面
                roomSelectionPage.classList.add('hidden');
                screenSharePage.classList.remove('hidden');

                console.log("加入按钮被点击, roomId: " + roomId);
                // 初始化本地码流
                initLocalStream();
            }
        });
        
        // 离开房间
        leaveRoomBtn.addEventListener('click', () => {
            // 停止所有流
            if (localStream) {
                localStream.getTracks().forEach(track => track.stop());
                localStream = null;
            }
            
            if (remoteStream) {
                remoteStream.getTracks().forEach(track => track.stop());
                remoteStream = null;
            }

            doLeave();
            
            // 重置UI
            resetUI();
            
            // 返回房间选择页面
            screenSharePage.classList.add('hidden');
            roomSelectionPage.classList.remove('hidden');
        });
        
        // 开始共享屏幕
        startShareBtn.addEventListener('click', async () => {
            try {
                doStart(roomId);
                // 将流连接到视频元素
                localVideo.srcObject = localStream;
                
                // 隐藏初始覆盖层
                localOverlay.classList.add('opacity-0', 'pointer-events-none');
                
                // 更新UI状态
                startShareBtn.innerHTML = '<i class="fa fa-stop"></i><span>停止共享</span>';
                startShareBtn.classList.remove('bg-primary');
                startShareBtn.classList.add('bg-danger');
                
                // 添加流结束事件监听（用户在系统层面停止共享）
                localStream.getVideoTracks()[0].addEventListener('ended', () => {
                    stopSharing();
                });
                
            } catch (error) {
                console.error('获取屏幕共享流失败:', error);
                alert('无法开始屏幕共享: ' + error.message);
            }
        });
        
        // 停止共享
        function stopSharing() {
            if (localStream) {
                localStream.getTracks().forEach(track => track.stop());
                doEnd(roomId);
                // 重置UI
                localVideo.srcObject = null;
                localOverlay.classList.remove('opacity-0', 'pointer-events-none');
                
                startShareBtn.innerHTML = '<i class="fa fa-desktop"></i><span>开始共享</span>';
                startShareBtn.classList.remove('bg-danger');
                startShareBtn.classList.add('bg-primary');
                
                // 如果远程流是克隆的本地流，也停止它
                if (remoteStream) {
                    remoteStream.getTracks().forEach(track => track.stop());
                    remoteStream = null;
                    remoteVideo.srcObject = null;
                    remoteOverlay.classList.remove('opacity-0', 'pointer-events-none');
                    mainStatusText.textContent = '对方尚未开始共享屏幕';
                }
            }
        }
        
        // 交换视频
        swapVideoBtn.addEventListener('click', () => {
            isSwapped = !isSwapped;
            
            if (isSwapped) {
                // 交换流
                if (localStream && remoteStream) {
                    localVideo.srcObject = remoteStream;
                    remoteVideo.srcObject = localStream;
                    
                    // 交换标题
                    [localTitle.textContent, remoteTitle.textContent] = [remoteTitle.textContent, localTitle.textContent];
                    
                    // 交换UI类
                    mainVideoContainer.classList.remove('lg:col-span-2');
                    mainVideoContainer.classList.add('lg:col-span-1');
                    localVideoContainer.classList.remove('lg:col-span-1');
                    localVideoContainer.classList.add('lg:col-span-2');
                    
                    // 动画效果
                    mainVideoContainer.classList.add('scale-95');
                    localVideoContainer.classList.add('scale-95');
                    
                    setTimeout(() => {
                        mainVideoContainer.classList.remove('scale-95');
                        localVideoContainer.classList.remove('scale-95');
                    }, 100);
                }
            } else {
                // 恢复原始状态
                if (localStream && remoteStream) {
                    localVideo.srcObject = localStream;
                    remoteVideo.srcObject = remoteStream;
                    
                    // 恢复标题
                    [localTitle.textContent, remoteTitle.textContent] = [remoteTitle.textContent, localTitle.textContent];
                    
                    // 恢复UI类
                    mainVideoContainer.classList.remove('lg:col-span-1');
                    mainVideoContainer.classList.add('lg:col-span-2');
                    localVideoContainer.classList.remove('lg:col-span-2');
                    localVideoContainer.classList.add('lg:col-span-1');
                    
                    // 动画效果
                    mainVideoContainer.classList.add('scale-95');
                    localVideoContainer.classList.add('scale-95');
                    
                    setTimeout(() => {
                        mainVideoContainer.classList.remove('scale-95');
                        localVideoContainer.classList.remove('scale-95');
                    }, 100);
                }
            }
        });
        
        // 全屏切换
        toggleFullscreenBtn.addEventListener('click', toggleFullscreen);
        
        // 双击主窗口进入全屏
        mainVideoContainer.addEventListener('click', (e) => {
            const now = Date.now();
            const delta = now - lastClickTime;
            
            if (delta < 300) { // 300ms内的两次点击被视为双击
                toggleFullscreen();
            }
            
            lastClickTime = now;
        });
        
        // 全屏切换函数
        function toggleFullscreen() {
            if (!document.fullscreenElement) {
                mainVideoContainer.requestFullscreen().catch(err => {
                    console.error(`全屏请求错误: ${err.message}`);
                });
                toggleFullscreenBtn.innerHTML = '<i class="fa fa-compress"></i>';
            } else {
                document.exitFullscreen();
                toggleFullscreenBtn.innerHTML = '<i class="fa fa-expand"></i>';
            }
        }
        
        // 监听全屏状态变化
        document.addEventListener('fullscreenchange', () => {
            if (!document.fullscreenElement) {
                toggleFullscreenBtn.innerHTML = '<i class="fa fa-expand"></i>';
            }
        });
        
        // 更新连接状态
        function updateConnectionStatus(text, iconColor) {
            connectionStatus.innerHTML = `
                <i class="fa fa-circle ${iconColor} mr-2 text-xs"></i>
                ${text}
            `;
        }
        
        // 重置UI
        function resetUI() {
            // 重置视频元素
            localVideo.srcObject = null;
            remoteVideo.srcObject = null;
            
            // 显示初始覆盖层
            localOverlay.classList.remove('opacity-0', 'pointer-events-none');
            remoteOverlay.classList.remove('opacity-0', 'pointer-events-none');
            
            // 重置按钮状态
            startShareBtn.innerHTML = '<i class="fa fa-desktop"></i><span>开始共享</span>';
            startShareBtn.classList.remove('bg-danger');
            startShareBtn.classList.add('bg-primary');
            
            // 重置视频容器类
            mainVideoContainer.classList.remove('lg:col-span-1');
            mainVideoContainer.classList.add('lg:col-span-2');
            localVideoContainer.classList.remove('lg:col-span-2');
            localVideoContainer.classList.add('lg:col-span-1');
            
            // 重置标题
            remoteTitle.textContent = '对方的屏幕';
            localTitle.textContent = '我的屏幕';
            
            // 重置状态
            isSwapped = false;
            updateConnectionStatus('等待连接', 'text-yellow-500');
            mainStatusText.textContent = '等待对方加入房间';
            
            // 退出全屏
            if (document.fullscreenElement) {
                document.exitFullscreen();
            }
        }
        
        // 检查浏览器兼容性
        if (!navigator.mediaDevices || !navigator.mediaDevices.getDisplayMedia) {
            alert('您的浏览器不支持屏幕共享功能');
            startShareBtn.disabled = true;
            startShareBtn.classList.add('opacity-50', 'cursor-not-allowed');
        }

        function handleIceCandidate(event) {
            console.info("handleIceCandidate");
            if (event.candidate) {
                var jsonMsg = {
                    'cmd': SIGNAL_TYPE_CANDIDATE,
                    'roomId': roomId,
                    'uid': localUserId,
                    'remoteUid': remoteUserId,
                    'msg': JSON.stringify(event.candidate)
                };
                var message = JSON.stringify(jsonMsg);
                RTCEngine.sendMessage(message);
                // console.info("handleIceCandidate message: " + message);
                console.info("send candidate message");
            } else {
                console.warn("End of candidates");
            }
        }

        function handleRemoteStreamAdd(event) {
            console.info("handleRemoteStreamAdd");
            remoteStream = event.streams[0];
            // remoteVideo.srcObject = remoteStream;
        }

        function handleConnectionStateChange() {
            if(pc != null) {
                console.info("ConnectionState -> " + pc.connectionState);
            }
        }

        function handleIceConnectionStateChange() {
            if(pc != null) {
                console.info("IceConnectionState -> " + pc.iceConnectionState);
            }
        }

        function createPeerConnection() {
            console.log("createPeerConnection")
            var defaultConfiguration = {  
                // bundlePolicy: "max-bundle",
                rtcpMuxPolicy: "require",
                iceTransportPolicy:"relay",//relay 或者 all
                // 修改ice数组测试效果，需要进行封装
                iceServers: [
                    {
                        "urls": [
                            "turn:192.168.3.186:3478?transport=udp",
                            "turn:192.168.3.186:3478?transport=tcp"       // 可以插入多个进行备选
                        ],
                        "username": "achene",
                        "credential": "123456"
                    },
                    {
                        "urls": [
                            "stun:192.168.3.186:3478"
                        ]
                    }
                ]
            };

            pc = new RTCPeerConnection(defaultConfiguration);
            pc.onicecandidate = handleIceCandidate;
            pc.ontrack = handleRemoteStreamAdd;
            pc.onconnectionstatechange = handleConnectionStateChange;
            pc.oniceconnectionstatechange = handleIceConnectionStateChange

            localStream.getTracks().forEach((track) => pc.addTrack(track, localStream));
        }

        function createOfferAndSendMessage(session) {
            pc.setLocalDescription(session)
                .then(function () {
                    var jsonMsg = {
                        'cmd': 'offer',
                        'roomId': roomId,
                        'uid': localUserId,
                        'remoteUid': remoteUserId,
                        'msg': JSON.stringify(session)
                    };
                    var message = JSON.stringify(jsonMsg);
                    RTCEngine.sendMessage(message);
                    console.info("send offer message");
                })
                .catch(function (error) {
                    console.error("offer setLocalDescription failed: " + error);
                });

        }

        function handleCreateOfferError(error) {
            console.error("handleCreateOfferError: " + error);
        }

        function createAnswerAndSendMessage(session) {
            pc.setLocalDescription(session)
                .then(function () {
                    var jsonMsg = {
                        'cmd': SIGNAL_TYPE_ANSWER,
                        'roomId': roomId,
                        'uid': localUserId,
                        'remoteUid': remoteUserId,
                        'msg': JSON.stringify(session)
                    };
                    var message = JSON.stringify(jsonMsg);
                    RTCEngine.sendMessage(message);
                    console.info("send answer message");
                })
                .catch(function (error) {
                    console.error("answer setLocalDescription failed: " + error);
                });

        }

        function handleCreateAnswerError(error) {
            console.error("handleCreateAnswerError: " + error);
        }

        var RTCEngine = function (wsUrl) {
            this.init(wsUrl);
            RTCEngine = this;
            return this;
        }

        RTCEngine.prototype.init = function (wsUrl) {
            // 设置websocket  url
            this.wsUrl = wsUrl;
            /** websocket对象 */
            this.signaling = null;
        }

        RTCEngine.prototype.createWebsocket = function () {
            RTCEngine = this;
            RTCEngine.signaling = new WebSocket(this.wsUrl);

            RTCEngine.signaling.onopen = function () {
                RTCEngine.onOpen();
            }

            RTCEngine.signaling.onmessage = function (ev) {
                RTCEngine.onMessage(ev);
            }

            RTCEngine.signaling.onerror = function (ev) {
                RTCEngine.onError(ev);
            }

            RTCEngine.signaling.onclose = function (ev) {
                RTCEngine.onClose(ev);
            }
        }

        RTCEngine.prototype.onOpen = function () {
            console.log("websocket open");
        }
        RTCEngine.prototype.onMessage = function (event) {
            console.log("onMessage: " + event.data);
            var jsonMsg = null;
            try {
                jsonMsg = JSON.parse(event.data);
            } catch(e) {
                console.warn("onMessage parse Json failed:" + e);
                return;
            }

            switch (jsonMsg.cmd) {
                case SIGNAL_TYPE_NEW_PEER:
                    handleRemoteNewPeer(jsonMsg);
                    break;
                case SIGNAL_TYPE_RESP_JOIN:
                    handleResponseJoin(jsonMsg);
                    break;
                case SIGNAL_TYPE_PEER_LEAVE:
                    handleRemotePeerLeave(jsonMsg);
                    break;
                case SIGNAL_TYPE_OFFER:
                    handleRemoteOffer(jsonMsg);
                    break;
                case SIGNAL_TYPE_ANSWER:
                    handleRemoteAnswer(jsonMsg);
                    break;
                case SIGNAL_TYPE_CANDIDATE:
                    handleRemoteCandidate(jsonMsg);
                    break;
                case SIGNAL_TYPE_START:
                    handleRtmoteStart(jsonMsg);
                    break;
                case SIGNAL_TYPE_END:
                    handleRtmoteEnd(jsonMsg);
                    break;
            }
        }

        RTCEngine.prototype.onError = function (event) {
            console.log("onError: " + event.data);
        }

        RTCEngine.prototype.onClose = function (event) {
            console.log("onClose -> code: " + event.code + ", reason:" + EventTarget.reason);
        }

        RTCEngine.prototype.sendMessage = function (message) {
            this.signaling.send(message);
        }

        function handleRtmoteEnd(message) {
            console.info("handleRtmoteEnd");
            mainStatusText.textContent = '对方未开启共享'
            remoteVideo.srcObject = null;
        }

        function handleRtmoteStart(message) {
            console.info("handleRtmoteStart");
            mainStatusText.textContent = '对方开启共享'
            remoteVideo.srcObject = remoteStream;
            remoteOverlay.classList.add('opacity-0', 'pointer-events-none');
        }

        function handleResponseJoin(message) {
            console.info("handleResponseJoin, remoteUid: " + message.remoteUid);
            remoteUserId = message.remoteUid;
        }

        function handleRemotePeerLeave(message) {
            console.info("handleRemotePeerLeave, remoteUid: " + message.remoteUid);
            remoteVideo.srcObject = null;
            if(pc != null) {
                pc.close();
                pc = null;
            }
        }

        function handleRemoteNewPeer(message) {
            console.info("handleRemoteNewPeer, remoteUid: " + message.remoteUid);
            remoteUserId = message.remoteUid;
            doOffer();
        }

        function handleRemoteOffer(message) {
            console.info("handleRemoteOffer");
            if(pc == null) {
                createPeerConnection();
            }
            var desc = JSON.parse(message.msg);
            pc.setRemoteDescription(desc);
            doAnswer();
        }

        function handleRemoteAnswer(message) {
            console.info("handleRemoteAnswer");
            var desc = JSON.parse(message.msg);
            pc.setRemoteDescription(desc);
        }

        function handleRemoteCandidate(message) {
            updateConnectionStatus('已连接', 'text-green-500');
            mainStatusText.textContent = '对方尚未开始共享屏幕';
            console.info("handleRemoteCandidate");
            var candidate = JSON.parse(message.msg);
            pc.addIceCandidate(candidate).catch(e => {
                console.error("addIceCandidate failed:" + e.name);
            });
        }

        function doOffer() {
            // 创建RTCPeerConnection
            console.log("dooffer")
            if (pc == null) {
                createPeerConnection();
            }
            pc.createOffer().then(createOfferAndSendMessage).catch(handleCreateOfferError);
        }

        function doAnswer() {
            pc.createAnswer().then(createAnswerAndSendMessage).catch(handleCreateAnswerError);
        }

        function doStart(roomId) {
            var jsonMsg = {
                'cmd': SIGNAL_TYPE_START,
                'roomId': roomId,
                'uid': localUserId,
                'remoteUid': remoteUserId
            };
            var message = JSON.stringify(jsonMsg);
            RTCEngine.sendMessage(message);
            console.info("doStart");
        }

        function doEnd(roomId) {
            var jsonMsg = {
                'cmd': SIGNAL_TYPE_END,
                'roomId': roomId,
                'uid': localUserId,
                'remoteUid': remoteUserId
            };
            var message = JSON.stringify(jsonMsg);
            RTCEngine.sendMessage(message);
            console.info("doEnd");
        }

        function doJoin(roomId) {
            var jsonMsg = {
                'cmd': SIGNAL_TYPE_JOIN,
                'roomId': roomId,
                'uid': localUserId,
            };
            var message = JSON.stringify(jsonMsg);
            RTCEngine.sendMessage(message);
            console.info("doJoin");
        }

        function doLeave() {
            var jsonMsg = {
                'cmd': SIGNAL_TYPE_LEAVE,
                'roomId': roomId,
                'uid': localUserId,
            };
            var message = JSON.stringify(jsonMsg);
            RTCEngine.sendMessage(message);
            console.info("doLeave");
            hangup();
        }

        function hangup() {
            localVideo.srcObject = null; // 0.关闭自己的本地显示
            remoteVideo.srcObject = null; // 1.不显示对方
            closeLocalStream(); // 2. 关闭本地流
            if(pc != null) {
                pc.close(); // 3.关闭RTCPeerConnection
                pc = null;
            }
        }

        function closeLocalStream() {
            if(localStream != null) {
                localStream.getTracks().forEach((track) => {
                        track.stop();
                });
            }
        }

        function openLocalStream(stream) {
            console.log('Open local stream');
            doJoin(roomId);
            localVideo.srcObject = stream;
            localStream = stream;
        }


        function initLocalStream() {
            navigator.mediaDevices.getDisplayMedia({
                    video: {
                        cursor: "always" // 显示鼠标指针
                    },
                    audio: false // 不捕获音频
                })
            .then(openLocalStream)
            .catch(function (e) {
                alert("getUserMedia() error: " + e.name);
            });
        }

        RTCEngine = new RTCEngine("ws://192.168.3.186:8099");
        RTCEngine.createWebsocket();

    </script>
</body>
</html>